//@version=6
// © ProjectSyndicate
indicator("Market Structure Signals", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500)

// ========================================
// USER INPUTS
// ========================================

// Market Structure Settings
swingLength = input.int(25, "Market Structure Lookback", minval=5, maxval=100, tooltip="Number of candles for pivot detection", group="Market Structure")
bosConfirmation = input.string('Close', 'BOS Confirmation', options=['Close', 'Wick'], tooltip='Use candle close or wick for breakout confirmation', group="Market Structure")
showCHoCH = input.bool(true, 'Show CHoCH Labels', tooltip='Display Change of Character labels', group="Market Structure")

// TP/SL Settings based on ADR10
tp1Percent = input.float(20.0, "TP1 % of ADR10", minval=1.0, step=1.0, tooltip="TP1 distance as percentage of 10-day Average Daily Range", group="Targets")
tp2Percent = input.float(30.0, "TP2 % of ADR10", minval=1.0, step=1.0, tooltip="TP2 distance as percentage of 10-day Average Daily Range", group="Targets")
slPercent = input.float(20.0, "SL % of ADR10", minval=1.0, step=1.0, tooltip="Stop Loss distance as percentage of 10-day Average Daily Range", group="Targets")

// Color Theme (from reference script)
colorBullish = input.color(#21c997, "Bullish Color", inline="c1", group="Colors")
colorBearish = input.color(#cc24e2, "Bearish Color", inline="c1", group="Colors")
colorTP1 = input.color(#26a69a, "TP1 Color", inline="c2", group="Colors")
colorTP2 = input.color(#00897b, "TP2 Color", inline="c2", group="Colors")
colorSL = input.color(#b71c1c, "SL Color", inline="c3", group="Colors")

boxTransparency = input.int(85, "Zone Transparency", minval=0, maxval=100, group="Colors")
borderTransparency = input.int(50, "Border Transparency", minval=0, maxval=100, group="Colors")

// Dashboard Settings
dashboardTextSize = input.string("Normal", "Dashboard Text Size", options=["Tiny", "Small", "Normal", "Large", "Huge"], tooltip="Adjust text size for better visibility on different screen sizes", group="Dashboard")

// ========================================
// MARKET STRUCTURE DETECTION
// ========================================

// Detect pivot highs and lows
pivotHigh = ta.pivothigh(high, swingLength, swingLength)
pivotLow = ta.pivotlow(low, swingLength, swingLength)

// Track previous swing points
var float lastHigh = na
var float lastLow = na
var int lastHighBar = na
var int lastLowBar = na
var bool highActive = false
var bool lowActive = false
var int lastBreakDirection = 0  // 1 = bullish, -1 = bearish

// Update swing points when new pivots form
if not na(pivotHigh)
    lastHigh := pivotHigh
    lastHighBar := bar_index - swingLength
    highActive := true

if not na(pivotLow)
    lastLow := pivotLow
    lastLowBar := bar_index - swingLength
    lowActive := true

// Determine breakout source based on confirmation type
float breakoutSourceHigh = bosConfirmation == 'Close' ? close : high
float breakoutSourceLow = bosConfirmation == 'Close' ? close : low

// Detect breakouts
bool bullishBreakout = false
bool bearishBreakout = false

if highActive and not na(lastHigh)
    if breakoutSourceHigh > lastHigh
        bullishBreakout := true
        highActive := false

if lowActive and not na(lastLow)
    if breakoutSourceLow < lastLow
        bearishBreakout := true
        lowActive := false

// ========================================
// SIGNAL GENERATION
// ========================================

// Determine if signal is BOS or CHoCH
bool isBullishCHoCH = bullishBreakout and lastBreakDirection == -1
bool isBearishCHoCH = bearishBreakout and lastBreakDirection == 1

// Update last break direction
if bullishBreakout
    lastBreakDirection := 1
if bearishBreakout
    lastBreakDirection := -1

// Draw breakout lines and labels
if bullishBreakout and not na(lastHigh) and not na(lastHighBar)
    // Draw horizontal line at breakout level
    line.new(lastHighBar, lastHigh, bar_index, lastHigh, color=colorBullish, width=2)
    
    // Label: BOS or CHoCH
    string labelText = (isBullishCHoCH and showCHoCH) ? 'CHoCH' : 'BOS'
    int labelBar = int((lastHighBar + bar_index) / 2)
    label.new(labelBar, lastHigh, labelText, 
              color=color.new(colorBullish, 20), 
              textcolor=color.white, 
              size=size.large, 
              style=label.style_label_down)

if bearishBreakout and not na(lastLow) and not na(lastLowBar)
    // Draw horizontal line at breakout level
    line.new(lastLowBar, lastLow, bar_index, lastLow, color=colorBearish, width=2)
    
    // Label: BOS or CHoCH
    string labelText = (isBearishCHoCH and showCHoCH) ? 'CHoCH' : 'BOS'
    int labelBar = int((lastLowBar + bar_index) / 2)
    label.new(labelBar, lastLow, labelText, 
              color=color.new(colorBearish, 20), 
              textcolor=color.white, 
              size=size.large, 
              style=label.style_label_up)

// ========================================
// TP/SL CALCULATION & VISUALIZATION
// ========================================

// Calculate ADR10 (10-day Average Daily Range)
float dailyRange = request.security(syminfo.tickerid, "D", high - low, lookahead=barmerge.lookahead_off)
float adr10 = ta.sma(dailyRange, 10)

// Calculate TP/SL distances based on ADR10 percentages
float tp1Distance = adr10 * (tp1Percent / 100)
float tp2Distance = adr10 * (tp2Percent / 100)
float slDistance = adr10 * (slPercent / 100)

// Track active trade levels
var float activeEntry = na
var float activeSL = na
var float activeTP1 = na
var float activeTP2 = na
var int activeDirection = 0  // 1 = long, -1 = short
var int entryBar = na

// Arrays for visualization
var array<box> tpBoxes = array.new<box>()
var array<label> tpLabels = array.new<label>()

// Statistics tracking
var int totalSignals = 0
var int totalClosedTrades = 0
var int tp1Wins = 0
var int tp2Wins = 0
var int slLosses = 0
var array<string> tradeHistory = array.new<string>()

// Function to record trade result
recordTrade(string result) =>
    array.unshift(tradeHistory, result)
    if array.size(tradeHistory) > 10
        array.pop(tradeHistory)

// Close previous trade if new signal arrives
if (bullishBreakout or bearishBreakout) and activeDirection != 0
    // Trade was closed by reversal - count as loss
    slLosses += 1
    totalClosedTrades += 1
    recordTrade("Reversal")
    activeDirection := 0

// Setup new trade on bullish breakout
if bullishBreakout and not na(lastHigh)
    activeEntry := lastHigh
    activeSL := lastHigh - slDistance
    activeTP1 := lastHigh + tp1Distance
    activeTP2 := lastHigh + tp2Distance
    activeDirection := 1
    entryBar := bar_index
    totalSignals += 1
    
    // Create TP1 box
    box tp1Box = box.new(entryBar, activeTP1 + (slDistance * 0.1), bar_index, activeTP1 - (slDistance * 0.1),
                         bgcolor=color.new(colorTP1, boxTransparency),
                         border_color=color.new(colorTP1, borderTransparency),
                         border_width=1)
    array.push(tpBoxes, tp1Box)
    
    label tp1Label = label.new(bar_index, activeTP1, "TP1: " + str.tostring(activeTP1, format.mintick),
                               color=color.new(colorTP1, 20),
                               textcolor=color.white,
                               size=size.small,
                               style=label.style_label_left)
    array.push(tpLabels, tp1Label)
    
    // Create TP2 box
    box tp2Box = box.new(entryBar, activeTP2 + (slDistance * 0.1), bar_index, activeTP2 - (slDistance * 0.1),
                         bgcolor=color.new(colorTP2, boxTransparency),
                         border_color=color.new(colorTP2, borderTransparency),
                         border_width=1)
    array.push(tpBoxes, tp2Box)
    
    label tp2Label = label.new(bar_index, activeTP2, "TP2: " + str.tostring(activeTP2, format.mintick),
                               color=color.new(colorTP2, 20),
                               textcolor=color.white,
                               size=size.small,
                               style=label.style_label_left)
    array.push(tpLabels, tp2Label)
    
    // Create SL box
    box slBox = box.new(entryBar, activeSL + (slDistance * 0.1), bar_index, activeSL - (slDistance * 0.1),
                        bgcolor=color.new(colorSL, boxTransparency),
                        border_color=color.new(colorSL, borderTransparency),
                        border_width=1)
    array.push(tpBoxes, slBox)
    
    label slLabel = label.new(bar_index, activeSL, "SL: " + str.tostring(activeSL, format.mintick),
                              color=color.new(colorSL, 20),
                              textcolor=color.white,
                              size=size.small,
                              style=label.style_label_left)
    array.push(tpLabels, slLabel)
    
    // Entry label
    label entryLabel = label.new(bar_index, activeEntry, "Entry: " + str.tostring(activeEntry, format.mintick),
                                 color=color.new(colorBullish, 20),
                                 textcolor=color.white,
                                 size=size.small,
                                 style=label.style_label_left)
    array.push(tpLabels, entryLabel)

// Setup new trade on bearish breakout
if bearishBreakout and not na(lastLow)
    activeEntry := lastLow
    activeSL := lastLow + slDistance
    activeTP1 := lastLow - tp1Distance
    activeTP2 := lastLow - tp2Distance
    activeDirection := -1
    entryBar := bar_index
    totalSignals += 1
    
    // Create TP1 box
    box tp1Box = box.new(entryBar, activeTP1 + (slDistance * 0.1), bar_index, activeTP1 - (slDistance * 0.1),
                         bgcolor=color.new(colorTP1, boxTransparency),
                         border_color=color.new(colorTP1, borderTransparency),
                         border_width=1)
    array.push(tpBoxes, tp1Box)
    
    label tp1Label = label.new(bar_index, activeTP1, "TP1: " + str.tostring(activeTP1, format.mintick),
                               color=color.new(colorTP1, 20),
                               textcolor=color.white,
                               size=size.small,
                               style=label.style_label_left)
    array.push(tpLabels, tp1Label)
    
    // Create TP2 box
    box tp2Box = box.new(entryBar, activeTP2 + (slDistance * 0.1), bar_index, activeTP2 - (slDistance * 0.1),
                         bgcolor=color.new(colorTP2, boxTransparency),
                         border_color=color.new(colorTP2, borderTransparency),
                         border_width=1)
    array.push(tpBoxes, tp2Box)
    
    label tp2Label = label.new(bar_index, activeTP2, "TP2: " + str.tostring(activeTP2, format.mintick),
                               color=color.new(colorTP2, 20),
                               textcolor=color.white,
                               size=size.small,
                               style=label.style_label_left)
    array.push(tpLabels, tp2Label)
    
    // Create SL box
    box slBox = box.new(entryBar, activeSL + (slDistance * 0.1), bar_index, activeSL - (slDistance * 0.1),
                        bgcolor=color.new(colorSL, boxTransparency),
                        border_color=color.new(colorSL, borderTransparency),
                        border_width=1)
    array.push(tpBoxes, slBox)
    
    label slLabel = label.new(bar_index, activeSL, "SL: " + str.tostring(activeSL, format.mintick),
                              color=color.new(colorSL, 20),
                              textcolor=color.white,
                              size=size.small,
                              style=label.style_label_left)
    array.push(tpLabels, slLabel)
    
    // Entry label
    label entryLabel = label.new(bar_index, activeEntry, "Entry: " + str.tostring(activeEntry, format.mintick),
                                 color=color.new(colorBearish, 20),
                                 textcolor=color.white,
                                 size=size.small,
                                 style=label.style_label_left)
    array.push(tpLabels, entryLabel)

// Monitor active trade - CHECK IN CORRECT ORDER ON SAME BAR
if activeDirection == 1  // Long trade
    bool tp2HitThisBar = high >= activeTP2
    bool tp1HitThisBar = high >= activeTP1
    bool slHitThisBar = low <= activeSL
    
    // Determine which was hit first by checking intrabar priority
    if tp2HitThisBar
        tp2Wins += 1
        tp1Wins += 1  // TP2 also counts as TP1 since it's further
        totalClosedTrades += 1
        recordTrade("TP2")
        activeDirection := 0
    else if tp1HitThisBar and not slHitThisBar
        tp1Wins += 1
        totalClosedTrades += 1
        recordTrade("TP1")
        activeDirection := 0
    else if slHitThisBar
        slLosses += 1
        totalClosedTrades += 1
        recordTrade("SL")
        activeDirection := 0

if activeDirection == -1  // Short trade
    bool tp2HitThisBar = low <= activeTP2
    bool tp1HitThisBar = low <= activeTP1
    bool slHitThisBar = high >= activeSL
    
    // Determine which was hit first
    if tp2HitThisBar
        tp2Wins += 1
        tp1Wins += 1  // TP2 also counts as TP1 since it's further
        totalClosedTrades += 1
        recordTrade("TP2")
        activeDirection := 0
    else if tp1HitThisBar and not slHitThisBar
        tp1Wins += 1
        totalClosedTrades += 1
        recordTrade("TP1")
        activeDirection := 0
    else if slHitThisBar
        slLosses += 1
        totalClosedTrades += 1
        recordTrade("SL")
        activeDirection := 0

// Extend boxes to current bar if trade is active
if activeDirection != 0 and array.size(tpBoxes) >= 4
    int lastIdx = array.size(tpBoxes) - 1
    box.set_right(array.get(tpBoxes, lastIdx), bar_index)
    box.set_right(array.get(tpBoxes, lastIdx - 1), bar_index)
    box.set_right(array.get(tpBoxes, lastIdx - 2), bar_index)
    box.set_right(array.get(tpBoxes, lastIdx - 3), bar_index)
    
    if array.size(tpLabels) >= 4
        int lastLblIdx = array.size(tpLabels) - 1
        label.set_x(array.get(tpLabels, lastLblIdx), bar_index)
        label.set_x(array.get(tpLabels, lastLblIdx - 1), bar_index)
        label.set_x(array.get(tpLabels, lastLblIdx - 2), bar_index)
        label.set_x(array.get(tpLabels, lastLblIdx - 3), bar_index)

// ========================================
// DASHBOARD
// ========================================

if barstate.islast
    // Determine text size based on user setting
    string textSizeHeader = dashboardTextSize == "Tiny" ? size.tiny : dashboardTextSize == "Small" ? size.small : dashboardTextSize == "Normal" ? size.normal : dashboardTextSize == "Large" ? size.large : size.huge
    string textSizeBody = dashboardTextSize == "Tiny" ? size.tiny : dashboardTextSize == "Small" ? size.small : dashboardTextSize == "Normal" ? size.small : dashboardTextSize == "Large" ? size.normal : size.large
    
    // Calculate win rates
    float tp1Rate = totalClosedTrades > 0 ? (tp1Wins / totalClosedTrades) * 100 : 0
    float tp2Rate = totalClosedTrades > 0 ? (tp2Wins / totalClosedTrades) * 100 : 0
    // Overall win rate = any TP hit (TP1 already includes TP2 hits)
    float overallWinRate = totalClosedTrades > 0 ? (tp1Wins / totalClosedTrades) * 100 : 0
    
    // Create dashboard table at top-right
    table dashboard = table.new(position.top_right, 2, 22, 
                                     bgcolor=color.new(#1e222d, 10),
                                     frame_color=color.new(#363a45, 50),
                                     frame_width=2,
                                     border_color=color.new(#363a45, 50),
                                     border_width=1)
    
    // Header
    table.cell(dashboard, 0, 0, "SMC SIGNALS DASHBOARD", 
               text_color=color.white, text_size=textSizeHeader, 
               bgcolor=color.new(#2962ff, 20))
    table.merge_cells(dashboard, 0, 0, 1, 0)
    
    // Statistics Section
    table.cell(dashboard, 0, 1, "STATISTICS", 
               text_color=color.new(color.white, 30), text_size=textSizeBody, 
               bgcolor=color.new(#363a45, 80))
    table.merge_cells(dashboard, 0, 1, 1, 1)
    
    table.cell(dashboard, 0, 2, "Total Signals", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 2, str.tostring(totalSignals), 
               text_color=color.new(#21c997, 0), text_size=textSizeBody)
    
    table.cell(dashboard, 0, 3, "Closed Trades", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 3, str.tostring(totalClosedTrades), 
               text_color=color.new(#21c997, 0), text_size=textSizeBody)
    
    table.cell(dashboard, 0, 4, "TP1 Wins", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 4, str.tostring(tp1Wins), 
               text_color=color.new(colorTP1, 0), text_size=textSizeBody)
    
    table.cell(dashboard, 0, 5, "TP2 Wins", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 5, str.tostring(tp2Wins), 
               text_color=color.new(colorTP2, 0), text_size=textSizeBody)
    
    table.cell(dashboard, 0, 6, "SL Losses", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 6, str.tostring(slLosses), 
               text_color=color.new(colorSL, 0), text_size=textSizeBody)
    
    // Win Rates Section
    table.cell(dashboard, 0, 7, "WIN RATES", 
               text_color=color.new(color.white, 30), text_size=textSizeBody, 
               bgcolor=color.new(#363a45, 80))
    table.merge_cells(dashboard, 0, 7, 1, 7)
    
    table.cell(dashboard, 0, 8, "TP1 Win Rate", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 8, str.format("{0,number,#.##}%", tp1Rate), 
               text_color=color.new(colorTP1, 0), text_size=textSizeBody)
    
    table.cell(dashboard, 0, 9, "TP2 Win Rate", 
               text_color=color.white, text_size=textSizeBody)
    table.cell(dashboard, 1, 9, str.format("{0,number,#.##}%", tp2Rate), 
               text_color=color.new(colorTP2, 0), text_size=textSizeBody)
    
    table.cell(dashboard, 0, 10, "Overall Win Rate", 
               text_color=color.white, text_size=textSizeBody)
    color winRateColor = overallWinRate >= 50 ? color.new(#21c997, 0) : color.new(#cc24e2, 0)
    table.cell(dashboard, 1, 10, str.format("{0,number,#.##}%", overallWinRate), 
               text_color=winRateColor, text_size=textSizeBody)
    
    // Last 10 Trades Section
    table.cell(dashboard, 0, 11, "LAST 10 TRADES", 
               text_color=color.new(color.white, 30), text_size=textSizeBody, 
               bgcolor=color.new(#363a45, 80))
    table.merge_cells(dashboard, 0, 11, 1, 11)
    
    int historySize = array.size(tradeHistory)
    if historySize > 0
        for i = 0 to math.min(historySize - 1, 9)
            string tradeResult = array.get(tradeHistory, i)
            color resultColor = tradeResult == "TP2" ? color.new(colorTP2, 0) : tradeResult == "TP1" ? color.new(colorTP1, 0) : tradeResult == "SL" or tradeResult == "Reversal" ? color.new(colorSL, 0) : color.new(color.gray, 0)
            
            table.cell(dashboard, 0, 12 + i, "Trade " + str.tostring(i + 1), 
                       text_color=color.white, text_size=textSizeBody)
            table.cell(dashboard, 1, 12 + i, tradeResult, 
                       text_color=resultColor, text_size=size.small,
                       bgcolor=color.new(resultColor, 90))
    else
        table.cell(dashboard, 0, 12, "No trades yet", 
                   text_color=color.new(color.white, 50), text_size=textSizeBody)
        table.merge_cells(dashboard, 0, 12, 1, 12)

// ========================================
// ALERTS
// ========================================

alertcondition(bullishBreakout, "Bullish BOS/CHoCH", "Bullish breakout detected")
alertcondition(bearishBreakout, "Bearish BOS/CHoCH", "Bearish breakout detected")
